// Copyright (C) 2004-2007 MySQL AB
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as published by
// the Free Software Foundation
//
// There are special exceptions to the terms and conditions of the GPL 
// as it is applied to this software. View the full text of the 
// exception in file EXCEPTIONS in the directory of this software 
// distribution.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections;

namespace MySql.Data.MySqlClient
{
	/// <summary>
	/// Summary description for PreparedStatement.
	/// </summary>
	internal class PreparedStatement
	{
		private Driver driver;
		private int statementId;
		private MySqlField[] paramList;
		private int executionCount;

		public PreparedStatement(Driver driver, int statementId, MySqlField[] paramList)
		{
			this.paramList = new MySqlField[0];
			this.driver = driver;
			this.statementId = statementId;
			this.paramList = paramList;
			executionCount = 0;
		}

		#region Properties

		public int StatementId
		{
			get { return statementId; }
		}

		public int NumParameters
		{
			get { return paramList.Length; }
		}

		public int ExecutionCount
		{
			get { return executionCount; }
			set { executionCount = value; }
		}

		#endregion

		public CommandResult Execute(MySqlParameterCollection parameters)
		{
			PacketWriter packet = new PacketWriter();
			packet.Driver = (NativeDriver)driver;

			//TODO: support long data here
			// create our null bitmap
			BitArray nullMap = new BitArray(parameters.Count); //metaData.Length );

			// now we run through the parameters that PREPARE sent back and use
			// those names to index into the parameters the user gave us.
			// if the user set that parameter to NULL, then we set the null map
			// accordingly
			for (int x = 0; x < paramList.Length; x++)
			{
				MySqlParameter p = parameters[paramList[x].ColumnName];
				if (p.Value == DBNull.Value || p.Value == null)
					nullMap[x] = true;
			}

			byte[] nullMapBytes = new byte[(parameters.Count + 7) / 8];
			// we check this because Mono doesn't ignore the case where nullMapBytes
			// is zero length.
			if (nullMapBytes.Length > 0)
				nullMap.CopyTo(nullMapBytes, 0);

			// start constructing our packet
			packet.WriteInteger(StatementId, 4);
			packet.WriteByte(0);          // flags; always 0 for 4.1
			packet.WriteInteger(1, 4);    // interation count; 1 for 4.1
			packet.Write(nullMapBytes);
			//if (parameters != null && parameters.Count > 0)
			packet.WriteByte(1);			// rebound flag
			//else
			//	packet.WriteByte( 0 );
			//TODO:  only send rebound if parms change

			// write out the parameter types
			foreach (MySqlField param in paramList)
			{
				MySqlParameter parm = parameters[param.ColumnName];
				packet.WriteInteger((long)parm.GetPSType(), 2);
			}

			// now write out all non-null values
			foreach (MySqlField param in paramList)
			{
				int index = parameters.IndexOf(param.ColumnName);
				if (index == -1)
					throw new MySqlException("Parameter '" + param.ColumnName +
						"' is not defined.");
				MySqlParameter parm = parameters[index];
				if (parm.Value == DBNull.Value || parm.Value == null) continue;

				packet.Encoding = param.Encoding;
				parm.Serialize(packet, true);
			}

			executionCount++;
			// send the data packet and return the CommandResult
			return driver.ExecuteStatement(((System.IO.MemoryStream)packet.Stream).ToArray());
		}

	}
}
